﻿using Dapper;
using Sprout.Core.Paging;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using static Dapper.SqlMapper;

namespace Sprout.Repository.Repository
{
    public static class RepositoryService
    {
        /// <summary>
        /// 实例参数
        /// </summary>
        /// <returns></returns>
        public static List<object> InitParameters()
        {
            return new List<object>();
        }

        /// <summary>
        /// 执行无返回值
        /// </summary>
        /// <param name="method">方法体</param>
        /// <param name="args">参数</param>
        public static void ExecuteNoReturn(MethodInfo method, List<object> args)
        {
            Execute(method, args);
        }
        /// <summary>
        /// 异步-执行无返回值
        /// </summary>
        /// <param name="method"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public static async Task ExecuteNoReturnAsync(MethodInfo method, List<object> args)
        {
            await ExecuteAsync(method, args);
        }
        /// <summary>
        /// 执行
        /// </summary>
        /// <param name="method">方法体</param>
        /// <param name="args">参数</param>
        /// <returns>受影响的行数</returns>
        public static int Execute(MethodInfo method, List<object> args)
        {
            var executeParams = GetExecuteParams(method);
            var parameters = GetParameters(method, args);
            var attribute = method.GetCustomAttribute<ExecuteAttribute>();

            var sqlResult = GetSqlResult(executeParams.sqlNameSpace, method.Name, attribute);
            if (sqlResult.isSql)
            {
                return DataService.ExecuteSql(attribute.Sql, parameters, executeParams.dataSource);
            }
            else
            {
                return DataService.Execute(sqlResult.statementId, parameters, executeParams.dataSource);
            }
        }
        /// <summary>
        /// 异步-执行
        /// </summary>
        /// <param name="method"></param>
        /// <param name="args"></param>
        /// <returns>受影响的行数</returns>
        public static async Task<int> ExecuteAsync(MethodInfo method, List<object> args)
        {
            var executeParams = GetExecuteParams(method);
            var parameters = GetParameters(method, args);
            var attribute = method.GetCustomAttribute<ExecuteAttribute>();
            var sqlResult = GetSqlResult(executeParams.sqlNameSpace, method.Name, attribute);
            if (sqlResult.isSql)
            {
                return await DataService.ExecuteSqlAsync(attribute.Sql, parameters, executeParams.dataSource);
            }
            else
            {
                return await DataService.ExecuteAsync(sqlResult.statementId, parameters, executeParams.dataSource);
            }
        }
        /// <summary>
        /// 执行
        /// </summary>
        /// <param name="method">方法体</param>
        /// <param name="args">参数</param>
        /// <returns>自增ID</returns>
        public static long ExecuteReturnId(MethodInfo method, List<object> args)
        {
            var executeParams = GetExecuteParams(method);
            var parameters = GetParameters(method, args);
            var attribute = method.GetCustomAttribute<ExecuteAttribute>();

            var sqlResult = GetSqlResult(executeParams.sqlNameSpace, method.Name, attribute);
            if (sqlResult.isSql)
            {
                return DataService.ExecuteReturnIdSql(attribute.Sql, parameters, executeParams.dataSource);
            }
            else
            {
                return DataService.ExecuteReturnId(sqlResult.statementId, parameters, executeParams.dataSource);
            }
        }
        /// <summary>
        /// 异步-执行
        /// </summary>
        /// <param name="method"></param>
        /// <param name="args"></param>
        /// <returns>自增ID</returns>
        public static async Task<long> ExecuteReturnIdAsync(MethodInfo method, List<object> args)
        {
            var executeParams = GetExecuteParams(method);
            var parameters = GetParameters(method, args);
            var attribute = method.GetCustomAttribute<ExecuteAttribute>();

            var sqlResult = GetSqlResult(executeParams.sqlNameSpace, method.Name, attribute);
            if (sqlResult.isSql)
            {
                return await DataService.ExecuteReturnIdSqlAsync(attribute.Sql, parameters, executeParams.dataSource);
            }
            else
            {
                return await DataService.ExecuteReturnIdAsync(sqlResult.statementId, parameters, executeParams.dataSource);
            }
        }
        /// <summary>
        /// 查询单个
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="method"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public static T QueryFirst<T>(MethodInfo method, List<object> args)
        {
            var executeParams = GetExecuteParams(method);
            var parameters = GetParameters(method, args);
            var attribute = method.GetCustomAttribute<SelectAttribute>();
            var sqlResult = GetSqlResult(executeParams.sqlNameSpace, method.Name, attribute);
            if (sqlResult.isSql)
            {
                return DataService.QueryFirstSql<T>(attribute.Sql, parameters, executeParams.dataSource);
            }
            else
            {
                return DataService.QueryFirst<T>(sqlResult.statementId, parameters, executeParams.dataSource);
            }
        }
        /// <summary>
        /// 异步查询单个
        /// </summary>
        /// <param name="method">方法体</param>
        /// <param name="args">参数</param>
        /// <returns></returns>
        public static async Task<T> QueryFirstAsync<T>(MethodInfo method, List<object> args)
        {
            var executeParams = GetExecuteParams(method);
            var parameters = GetParameters(method, args);
            var attribute = method.GetCustomAttribute<SelectAttribute>();
            var sqlResult = GetSqlResult(executeParams.sqlNameSpace, method.Name, attribute);
            if (sqlResult.isSql)
            {
                return await DataService.QueryFirstSqlAsync<T>(attribute.Sql, parameters, executeParams.dataSource);
            }
            else
            {
                return await DataService.QueryFirstAsync<T>(sqlResult.statementId, parameters, executeParams.dataSource);
            }
        }
        /// <summary>
        /// 分页查询
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="method"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public static PagedList<T> QueryPaging<T>(MethodInfo method, List<object> args)
        {
            var executeParams = GetExecuteParams(method);
            var parameters = GetParameters(method, args);
            var attribute = method.GetCustomAttribute<SelectAttribute>();
            var sqlResult = GetSqlResult(executeParams.sqlNameSpace, method.Name, attribute);
            var apiPageParameter = parameters as PageParameter;
            if (apiPageParameter == null)
            {
                throw new Exception("分页查询参数必须是ApiPageParameter或它的子类");
            }
            bool isCount = true;
            if (apiPageParameter.SkipIndex > 1 && attribute.CountOnlyFirst)
            {
                isCount = false;
            }
            if (sqlResult.isSql)
            {
                if (string.IsNullOrWhiteSpace(attribute.CountSql))
                {
                    return DataService.QueryPagingSql<T>(attribute.Sql, apiPageParameter, isCount, executeParams.dataSource);
                }
                else
                {
                    string countSql = isCount ? attribute.CountSql : string.Empty;
                    return DataService.QueryPagingSql<T>(attribute.Sql, countSql, apiPageParameter, executeParams.dataSource);
                }
            }
            else
            {
                if (string.IsNullOrWhiteSpace(attribute.CountStatementId))
                {
                    return DataService.QueryPaging<T>(sqlResult.statementId, apiPageParameter, isCount, executeParams.dataSource);
                }
                else
                {
                    string countStatementId = isCount ? attribute.CountStatementId : string.Empty;
                    return DataService.QueryPaging<T>(sqlResult.statementId, countStatementId, apiPageParameter, executeParams.dataSource);
                }
            }
        }

        public static async Task<PagedList<T>> QueryPagingAsync<T>(MethodInfo method, List<object> args)
        {
            var executeParams = GetExecuteParams(method);
            var parameters = GetParameters(method, args);
            var attribute = method.GetCustomAttribute<SelectAttribute>();
            var sqlResult = GetSqlResult(executeParams.sqlNameSpace, method.Name, attribute);
            var apiPageParameter = parameters as PageParameter;
            if (apiPageParameter == null)
            {
                throw new Exception("分页查询参数必须是ApiPageParameter或它的子类");
            }
            bool isCount = true;
            if (apiPageParameter.SkipIndex > 1 && attribute.CountOnlyFirst)
            {
                isCount = false;
            }
            if (sqlResult.isSql)
            {
                if (string.IsNullOrWhiteSpace(attribute.CountSql))
                {
                    return await DataService.QueryPagingSqlAsync<T>(attribute.Sql, apiPageParameter, isCount, executeParams.dataSource);
                }
                else
                {
                    string countSql = isCount ? attribute.CountSql : string.Empty;
                    return await DataService.QueryPagingSqlAsync<T>(attribute.Sql, countSql, apiPageParameter, executeParams.dataSource);
                }
            }
            else
            {
                if (string.IsNullOrWhiteSpace(attribute.CountStatementId))
                {
                    return await DataService.QueryPagingAsync<T>(sqlResult.statementId, apiPageParameter, isCount, executeParams.dataSource);
                }
                else
                {
                    string countStatementId = isCount ? attribute.CountStatementId : string.Empty;
                    return await DataService.QueryPagingAsync<T>(sqlResult.statementId, countStatementId, apiPageParameter, executeParams.dataSource);
                }
            }
        }
        /// <summary>
        /// 查询
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="method"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public static IList<T> QueryList<T>(MethodInfo method, List<object> args)
        {
            var executeParams = GetExecuteParams(method);
            var parameters = GetParameters(method, args);
            var attribute = method.GetCustomAttribute<SelectAttribute>();
            var sqlResult = GetSqlResult(executeParams.sqlNameSpace, method.Name, attribute);
            if (sqlResult.isSql)
            {
                return DataService.QuerySql<T>(attribute.Sql, parameters, executeParams.dataSource);
            }
            else
            {
                return DataService.Query<T>(sqlResult.statementId, parameters, executeParams.dataSource);
            }
        }
        /// <summary>
        /// 异步查询
        /// </summary>
        /// <param name="method">方法体</param>
        /// <param name="args">参数</param>
        /// <returns></returns>
        public static async Task<IList<T>> QueryListAsync<T>(MethodInfo method, List<object> args)
        {
            var executeParams = GetExecuteParams(method);
            var parameters = GetParameters(method, args);
            var attribute = method.GetCustomAttribute<SelectAttribute>();
            var sqlResult = GetSqlResult(executeParams.sqlNameSpace, method.Name, attribute);
            if (sqlResult.isSql)
            {
                return await DataService.QuerySqlAsync<T>(attribute.Sql, parameters, executeParams.dataSource);
            }
            else
            {
                return await DataService.QueryAsync<T>(sqlResult.statementId, parameters, executeParams.dataSource);
            }
        }




        #region 内部方法
        /// <summary>
        /// 获取参数
        /// </summary>
        /// <param name="method"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        private static object GetParameters(MethodInfo method, List<object> args)
        {
            //获取参数
            var dbArgs = new DynamicParameters();
            var parameterInfos = method.GetParameters();
            //如果只有一个参数，且参数为对象，则直接返回对象
            if (parameterInfos.Length == 1)
            {
                var parameterType = parameterInfos[0].ParameterType;
                //if ((parameterType.IsClass
                //    || typeof(IEnumerable).IsAssignableFrom(parameterType)
                //    || typeof(IDynamicParameters).IsAssignableFrom(parameterType)) && parameterType != typeof(string))
                //{
                //    return args[0];
                //}
                //else
                //{
                //    dbArgs.Add(parameterInfos[0].Name, args[0]);
                //}
                if (parameterType.IsValueType || parameterType == typeof(string))
                {
                    dbArgs.Add(parameterInfos[0].Name, args[0]);
                }
                else
                {
                    return args[0];
                }
            }
            else
            {
                for (var i = 0; i < parameterInfos.Length; i++)
                {
                    var parameterType = parameterInfos[i].ParameterType;

                    //如果是值类型或者字符串直接添加到参数里
                    if (parameterType.IsValueType || parameterType == typeof(string))
                    {
                        dbArgs.Add(parameterInfos[i].Name, args[i]);
                    }
                    ////如果是类，则读取属性值，然后添加到参数里
                    //else if (parameterType.IsClass)
                    //{
                    //    var properties = parameterType.GetTypeInfo().DeclaredProperties;
                    //    foreach (PropertyInfo info in properties)
                    //    {
                    //        var propertyType = info.PropertyType;
                    //        var propertyTypeIsString = propertyType.GetTypeInfo() == typeof(string);
                    //        if (propertyType.IsValueType || propertyTypeIsString)
                    //        {
                    //            dbArgs.Add(info.Name, info.GetValue(args[i]));
                    //        }
                    //    }
                    //}
                    //不支持简单类型+对象组成的参数
                    else
                    {
                        throw new Exception("不支持简单类型+对象组成的参数");
                    }
                }
            }

            return dbArgs;
        }
        /// <summary>
        /// 获取资源执行条件
        /// </summary>
        /// <param name="method"></param>
        /// <returns></returns>
        private static (string dataSource, string sqlNameSpace) GetExecuteParams(MethodInfo method)
        {
            string dataSource = null;
            string sqlNameSpace = null;
            string interfaceName = method.DeclaringType.FullName;
            var repositoryAttribute = method.DeclaringType.GetCustomAttribute<RepositoryAttribute>();
            if (repositoryAttribute != null)
            {
                dataSource = repositoryAttribute.DataSource;
                sqlNameSpace = string.IsNullOrEmpty(repositoryAttribute.SqlNameSpace) ? interfaceName : repositoryAttribute.SqlNameSpace;
            }
            else
            {
                sqlNameSpace = interfaceName;
            }
            return (dataSource, sqlNameSpace);
        }
        /// <summary>
        /// 执行语句是否是sql
        /// </summary>
        /// <param name="sqlNameSpace"></param>
        /// <param name="methodName"></param>
        /// <param name="attribute"></param>
        /// <returns></returns>
        private static (bool isSql, string statementId) GetSqlResult(string sqlNameSpace, string methodName, RepositoryActionAttribute attribute)
        {
            bool isSql = false;
            string statementId = string.Empty;
            if (string.IsNullOrWhiteSpace(attribute.Sql) == false)
            {
                isSql = true;
            }
            else if (string.IsNullOrWhiteSpace(attribute.StatementId) == false)
            {
                statementId = $"{sqlNameSpace}.{attribute.StatementId}";
            }
            else
            {
                statementId = $"{sqlNameSpace}.{methodName}";
            }
            return (isSql, statementId);
        }
        #endregion
    }
}
